﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using OmegaEngine;

namespace OmegaEngine
{
    public class Player : PhysicsActor
    {
        /* The values from here down, until otherwise noted, are not needed but show how */
        /* to add attributes to players */
        static int rand = 0;

        // Player Health
        private int health = 100;
        public event GUIValueHandler healthChanged;
        GUIDynamicLabel healthGUI;

        // Player Shields
        private int shields = 100;
        public event GUIValueHandler shieldsChanged;
        GUIDynamicLabel shieldsGUI;

        // Player Fuel (Used for Jetpacks)
        private int fuel = 1000;
        public event GUIValueHandler fuelChanged;
        GUIDynamicLabel fuelGUI;

        // The player's current pickup
        Pickup pickup;

        // Player speed modifier

        /* The values from here down are required for functionality */

        // Linked camera
        public Camera cam;

        // Functions for player stats
        public int Health { get { return health; } set { health = value; healthChanged(); } }
        public int Shields { get { return shields; } set { shields = value; shieldsChanged(); } }
        public int Fuel { get { return fuel; } set { fuel = value; fuelChanged(); } }

        // Constructors
        // Player without a camera, useful for bots
        public Player(Model Model, PhysicsObject PhysicsObject) : base(Model, PhysicsObject)
        {
            this.PhysicsObject = PhysicsObject;
            this.PhysicsObject.Mass = 1000;
            this.PhysicsObject.Immovable = false;
            this.PhysicsObject.Velocity = Engine.Services.GetService<Physics>().PhysicsSystem.Gravity;
            this.Scale = new Vector3(.01f);
        }

        // Player with a camera, for actual players
        public Player(Model Model, PhysicsObject PhysicsObject, Camera camera) : base(Model, PhysicsObject)
        {
            this.PhysicsObject = PhysicsObject;
            this.PhysicsObject.Mass = 1000;
            this.PhysicsObject.Immovable = false;
            this.PhysicsObject.Velocity = Engine.Services.GetService<Physics>().PhysicsSystem.Gravity;
            this.Scale = new Vector3(.01f);
            this.cam = camera;

            this.SetupGUI();
        }

        // Player with a camera, for actual players
        public Player(Model Model, PhysicsObject PhysicsObject, Camera camera, GameScreen Parent)
            : base(Model, PhysicsObject)
        {
            this.PhysicsObject = PhysicsObject;
            this.PhysicsObject.Mass = 1000;
            this.PhysicsObject.Immovable = false;
            this.PhysicsObject.Velocity = Engine.Services.GetService<Physics>().PhysicsSystem.Gravity;
            this.Scale = new Vector3(.01f);
            this.cam = camera;

            this.SetupGUI();
        }

        public override void Update()
        {
            if(Parent.IsInputAllowed)
                this.HandleInput();

            base.Update();
        }

        private void HandleInput()
        {
            GameScreen GS = Engine.GameScreens["Main"];
            KeyboardDevice keyboard = Engine.Services.GetService<KeyboardDevice>();
            MouseDevice mouse = Engine.Services.GetService<MouseDevice>();
            Camera cam = (Camera)this.cam;
            Physics phys = Engine.Services.GetService<Physics>();

            Vector3 inputModifier = new Vector3(
                (keyboard.IsKeyDown(Keys.A) ? 5 : 0) + (keyboard.IsKeyDown(Keys.D) ? -5 : 0),
                0,//(keyboard.IsKeyDown(Keys.Q) ? 1 : 0) + (keyboard.IsKeyDown(Keys.E) ? -1 : 0),
                (keyboard.IsKeyDown(Keys.W) ? 5 : 0) + (keyboard.IsKeyDown(Keys.S) ? -5 : 0)
                );

            Vector3 Rot = new Vector3(mouse.Delta.Y * -.002f, mouse.Delta.X * -.002f, 0);
            Vector3 Translation = ((inputModifier) * -.05f);

            cam.RotateTranslate(Rot, Vector3.Zero);
            if (!this.PhysicsObject.Body.IsActive)
                this.PhysicsObject.Body.SetActive();

            //double yaw = Math.Atan2(cam.Rotation.M32, cam.Rotation.M33);

            //Matrix rawr = MathUtil.Vector3ToMatrix(new Vector3(0, (float)yaw, 0));

            Vector3 movementVector = Vector3.Transform(inputModifier * -.02f, cam.Rotation);

            //movementVector.Z -= movementVector.Y;
            //movementVector.Y = 0;

            this.PhysicsObject.Velocity += movementVector;
            //this.PhysicsObject.Body.AddWorldForce(Vector3.Transform(inputModifier * -.05f, cam.Rotation));

            Vector3 newOffset = Vector3.Transform(cam.Offset, cam.Rotation);
            cam.Position = this.PhysicsObject.Position + newOffset;

            if (keyboard.WasKeyPressed(Keys.Space))
                this.PhysicsObject.Velocity += new Vector3(0, 8, 0);

            if (mouse.WasButtonPressed(MouseButtons.Left) || keyboard.WasKeyPressed(Keys.Q))
            {
                FallingBox act = new FallingBox(
                    Engine.Content.Load<Model>("Content/ig_box"),
                    new BoxObject(new Vector3(.5f), cam.Position, Vector3.Zero));

                act.Scale = new Vector3(.5f);
                act.PhysicsObject.Mass = 1000;

                Vector3 dir = cam.Target - cam.Position;
                dir.Normalize();

                act.PhysicsObject.Velocity = dir * 20;

                GS.AddComponent(act);
            }

            if (keyboard.WasKeyPressed(Keys.E))
            {
                rand++;

                Vector3 newGravity;

                switch ((rand % 6))
                {
                    case 0: newGravity = -10.0f * Vector3.Up; this.cam.Up = Vector3.Up; break;
                    case 1: newGravity = 10.0f * Vector3.Up; this.cam.Up = Vector3.Down; break;
                    case 2: newGravity = -10.0f * Vector3.Left; this.cam.Up = Vector3.Left; break;
                    case 3: newGravity = 10.0f * Vector3.Left; this.cam.Up = Vector3.Right; break;
                    case 4: newGravity = -10.0f * Vector3.Forward; this.cam.Up = Vector3.Forward; break;
                    case 5: newGravity = 10.0f * Vector3.Forward; this.cam.Up = Vector3.Backward; break;
                    default: newGravity = -10.0f * Vector3.Up; this.cam.Up = Vector3.Up; break;
                }

                phys.PhysicsSystem.Gravity = newGravity;
            }

            if (mouse.WasButtonHeld(MouseButtons.Right))
            {
                if (this.Fuel > 10)
                {
                    this.Fuel -= 10;

                    Vector3 newJet = phys.PhysicsSystem.Gravity;
                    newJet.Normalize();

                    this.PhysicsObject.Velocity += .3f * -newJet;
                }
            }

            if (this.Fuel < 1000)
                this.Fuel += 3;

            if (this.Fuel > 1000)
                this.Fuel = 1000;
        }

        // Sets up the Health, Shields and Fuel GUI, as well as the update events
        private void SetupGUI()
        {
            this.healthChanged = () => this.health.ToString();
            this.healthGUI = new GUIDynamicLabel("Health: ", this.healthChanged, Engine.DefaultFont, Engine.ScreenWidth - 110, Engine.ScreenHeight - 30, Color.Red);
            this.shieldsChanged = () => this.shields.ToString();
            this.shieldsGUI = new GUIDynamicLabel("Shield: ", this.shieldsChanged, Engine.DefaultFont, Engine.ScreenWidth - 240, Engine.ScreenHeight - 30, Color.Blue);
            this.fuelChanged = () => this.fuel.ToString();
            this.fuelGUI = new GUIDynamicLabel("Fuel: ", this.fuelChanged, Engine.DefaultFont, 10, Engine.ScreenHeight - 30, Color.Black);
        }
    }
}
